﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Imagine;
using Box2D.XNA;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Input;

namespace Carmack
{
    public enum eHeroAction
    {
        MoveForward,
        MoveBackward,
        MoveLeft,
        MoveRight,
        Fire,
        Reload,
        SwitchWeapon,
        Interact,
        Carry,
        Place,
        
        // ... More if we want
    }

    public enum eHeroState
    {
        Default,
        OnTurret,
        Carrying,
        InVehicle

        // ... More if we want
    }

    /// <summary>
    /// Our Protagonist
    /// </summary>
    public class CHero : CActor, IKeyboardListener, IMouseListener
    {
        public CHero(string texture): base(texture)
        {}
        
        public void Init( string gamerTag_ )
        {
            m_gamerTag = gamerTag_;         //To be used for Tagging

            //Register For Input Notifications
            TheInputManager.Instance().Register<IMouseListener>(this);
            TheInputManager.Instance().Register<IKeyboardListener>(this);

            //Create the Physics Body
            ActorPhysics.Friction = 1.0f;
            ActorPhysics.BodyType = BodyType.Dynamic;
            ActorPhysics.Position = new Vector2(Engine.Instance().ScreenWidth/2,Engine.Instance().ScreenHeight/2);
            ActorPhysics.InitPhysics( eShapeType.Shape_Circle, id );
            ActorPhysics.Body.SetUserData("Hero");
            ActorPhysics.HotSpot = eHotSpot.eCenter;
            m_velocity = new Vector2( 5.0f, 5.0f );

            //Setup Face and Weapon Direction
            Vector2 mousePos = TheInputManager.Instance().GetMousePos();
            Matrix cameraMat = Engine.Instance().Camera.GetTransformation();
            Vector2 finalPos = Vector2.Transform(mousePos, Matrix.Invert(cameraMat));            
            m_rotationAngle = VectorToAngle( finalPos );
            //m_currentWeapon.Direction = m_faceDirection;
  
            Flags |= (Byte)eGameObjectType.NetworkObject;

            //Init Weapon System and Set up Weapon Params
            CWeaponParams prm = new CWeaponParams(1.6f);
            m_weaponSystem = new CWeaponSystem();
            m_weaponSystem.InitWeapon(prm, eWeaponClass.Primary, "primary");
            prm.m_weaponClass = eWeaponClass.Secondary;
            prm.m_timer = new CGameTimer( 0.75, 0.75 );
            prm.m_rateOfFire = 0.6f;
            m_weaponSystem.InitWeapon(prm, eWeaponClass.Secondary, "Secondary");
            m_weaponSystem.SetCurrentWeapon(eWeaponClass.Primary);
            m_weaponSystem.m_primaryWeapon.weaponSprite.Position = ActorPhysics.Position;
            m_weaponSystem.m_primaryWeapon.weaponSprite.HotSpot = eHotSpot.eBottomCenter;
            m_weaponSystem.m_secondaryWeapon.weaponSprite.Position = ActorPhysics.Position;
            m_weaponSystem.m_secondaryWeapon.weaponSprite.HotSpot = eHotSpot.eBottomCenter;

            //Create States
            m_defaultState = new CHeroDefault();
            m_onTurretState = new CHeroOnTurret();
            m_carryingState = new CHeroCarrying();
            m_currentState = m_defaultState;

            //Create Actionmap for State management
            m_actionMap = new Dictionary<eHeroAction,Keys>();            

            //Now Transform the Camera to your Position
            TransformCamera();
        }
        
        public override bool Update(GameTime time)
        {
            //This handles Input Detection and other special updates
            m_currentState.Update(this, time);            

            base.Update(time);  
            // Transformation should happen after base.update
            TransformWeapon();  
            TransformCamera();                      

            m_weaponSystem.Update(time);            
            return true;
        }

        public override bool Draw(GameTime time)
        {
            ActorPhysics.Angle = m_rotationAngle;

            base.Draw(time);
            m_weaponSystem.Draw(time);
            return true;
        }

        public void EncodePacket(ref PacketWriter writer_)
        {
            writer_.Write( m_inputPacket );
            writer_.Write( m_rotationAngle );
        }

        //This function will simply add/remove <Key, Action> pairs         
        //that our Hero can do in a certain State
        public void SetState(eHeroState state_)
        {
            m_actionMap.Clear();
            Dictionary<eGameKeys,Keys> controlMap =  GameManager.Instance().ControlMap;
            switch (state_)
            {
                case eHeroState.Default:        //All Actions                    
                    m_actionMap.Add(eHeroAction.MoveForward, controlMap[eGameKeys.ForwardKey]);
                    m_actionMap.Add(eHeroAction.MoveLeft,controlMap[eGameKeys.LeftKey]);
                    m_actionMap.Add(eHeroAction.MoveBackward,controlMap[eGameKeys.BackwardKey]);
                    m_actionMap.Add(eHeroAction.MoveRight,controlMap[eGameKeys.RightKey]);
                    m_actionMap.Add(eHeroAction.Fire,controlMap[eGameKeys.FireKey]);
                    m_actionMap.Add(eHeroAction.Reload,controlMap[eGameKeys.ReloadKey]);
                    m_currentState = m_defaultState;
                    //m_currentState = eHeroState.Default;
                    break;

                case eHeroState.Carrying:
                    m_actionMap.Add(eHeroAction.MoveForward, controlMap[eGameKeys.ForwardKey]);
                    m_actionMap.Add(eHeroAction.MoveLeft, controlMap[eGameKeys.LeftKey]);
                    m_actionMap.Add(eHeroAction.MoveRight,controlMap[eGameKeys.RightKey]);
                    m_actionMap.Add(eHeroAction.MoveBackward,controlMap[eGameKeys.BackwardKey]);                    
                    m_actionMap.Add(eHeroAction.Place,controlMap[eGameKeys.PlaceKey]);                    
                    m_currentState = m_carryingState;
                break;
                case eHeroState.OnTurret:
                    m_currentState = m_onTurretState;
                    break;
            }
        
        }

        public void SetAction(eHeroAction action_)
        {
            
        }

        #region Properties
        public int InputPacket
        {
            get { return m_inputPacket;     }
            set { m_inputPacket = value;    }
        }
        public Dictionary<eHeroAction,Keys> ActionKeyMap
        {
            get { return m_actionMap;   }
        }
        public CWeaponSystem WeaponSystem
        {
            get {return m_weaponSystem;   }
        }
        public CWeapon CurrentWeapon
        {
            get { return m_weaponSystem.GetWeapon( m_weaponSystem.m_currentWeaponClass );  }
            
        }        
        #endregion

        #region Box2D Interface
        public void BeginContact(Contact contact)  {  }
        public void EndContact(Contact contact) { }
        public void PreSolve(Contact contact, ref Manifold oldManifold) { }
        public void PostSolve(Contact contact, ref ContactImpulse impulse) { }
        #endregion

        #region IKeyboardListner Interface
        public void KeyUp(Keys[] upKeys)    
        {
           m_currentState.KeyUp(this, upKeys);
        }
        public void KeyDown(Keys[] downKeys) 
        {            
            m_currentState.KeyDown(this, downKeys);
        }
        #endregion

        #region IMouseListner Interface
        public void MouseButtonDown(eMouseButtons[] downButton, Vector2 pos) 
        {
            m_currentState.MouseButtonDown(this, downButton,pos);
        }
        public void MouseButtonUp(eMouseButtons[] upButton, Vector2 pos) 
        {
            m_currentState.MouseButtonUp(this, upButton,pos);
        }
        public void MouseMove(Vector2 move)
        {
            m_currentState.MouseMove(this, move);
            
        }
        #endregion

        #region Private_parts
        string m_gamerTag;
        int m_inputPacket;        
         
        void TransformWeapon()
        {
            CurrentWeapon.weaponSprite.Position = ActorPhysics.Position;
            CurrentWeapon.weaponSprite.Angle = m_rotationAngle;
        }
        void TransformCamera()
        {
            Engine.Instance().Camera.Position = new Vector3( ActorPhysics.Position, 0.0f);
        }

        //I am using Action as key and Keys as value so that 
        //i know which action requires keys for polling 
        //and which requires keys only once
        Dictionary<eHeroAction,Keys> m_actionMap;       //Might be removed
        CWeaponSystem m_weaponSystem;
        #region States
        CHeroDefault    m_defaultState;
        CHeroOnTurret   m_onTurretState;
        CHeroCarrying   m_carryingState;
        IHeroState      m_currentState;
        #endregion

        #endregion
    }
}
